/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "GY_DiagramItem.h"
#include "GY_Arrow.h"

#include <QGraphicsScene>
#include <QGraphicsSceneContextMenuEvent>
#include <QMenu>
#include <QPainter>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QDebug>
#include <QFile>
#include <QUuid>
#include <QBitmap>
#include "GY_PortItem.h"
#include "GY_DiagramScene.h"
//! [0]
GY_DiagramItem::GY_DiagramItem(DiagramType diagramType, QMenu *contextMenu, QString str, QColor textColor, int fontPixelSize
                        ,QGraphicsItem *parent)
    : QGraphicsPolygonItem(parent)
    , myDiagramType(diagramType)
    , myContextMenu(contextMenu)
    , myTextColor(textColor)
    , myFontPixelSize(fontPixelSize)
{
    uuid = QUuid::createUuid().toString();
    width   =   150;
    height  =   0;


    setFlag(QGraphicsItem::ItemIsMovable, true);
    setFlag(QGraphicsItem::ItemIsSelectable, true);
    setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
//    setAcceptHoverEvents(true);

    //创建连线槽
    //strJson 格式："{"name":"","PI":[],"PO":[],"DI":[],"DO":[]}"。

    QJsonParseError e;
    QJsonDocument doc;
    //doc.fromJson(str.toUtf8(),&e);  //错误
    doc = QJsonDocument::fromJson(str.toUtf8(),&e);
    if(e.error == QJsonParseError::NoError){
        //qDebug()<<doc.isObject();  //
        QJsonObject obj = doc.object();

        {
            if(obj["type"].toString()=="Step"){
                myDiagramType=Step;
            }else if(obj["type"].toString()=="StartEnd"){
                myDiagramType=StartEnd;
            }else if(obj["type"].toString()=="Input"){
                myDiagramType=Input;
            }else if(obj["type"].toString()=="Output"){
                myDiagramType=Output;
            }else if(obj["type"].toString()=="Branch"){
                myDiagramType=Branch;
            }

        //    QPainterPath path;
            switch (myDiagramType) {
            case StartEnd:
                height = 50;
                break;
            case Step:
                height = 100;
                break;
            case Input:
            case Output:
                height = 100;
                break;
            case Branch:
                height = 100;
                break;

            default:
                break;
            }
//            path.moveTo(200, 50);
//            path.arcTo(150, 0, 50, 50, 0, 90);
//            path.arcTo(50, 0, 50, 50, 90, 90);
//            path.arcTo(50, 50, 50, 50, 180, 90);
//            path.arcTo(150, 50, 50, 50, 270, 90);
//            path.lineTo(200, 25);
//            myPolygon = path.toFillPolygon();
            //确保以后添加的内容不超出此区域
            myPolygon << QPointF(0, 0) << QPointF(width, 0)
                      << QPointF(width, height) << QPointF(0, height)
                      << QPointF(0, 0);
            setPolygon(myPolygon);
        }

        name = obj["name"].toString();
        if(obj.contains("uuid")){
            uuid = obj["uuid"].toString();
        }

        tips = obj["tips"].toString();
        setToolTip(QString("%1").arg(tips));

        int spacerHeight = 10;
        QJsonValue procI = obj["PI"];
        for(int i = 0;i < procI.toArray().size();++i){
            QJsonValue v = procI.toArray().at(i);
            GY_PortItem* port;
            port = new GY_PortItem(GY_PortItem::procI,v.toString());
            port->setParentItem(this);
            port->setPos(10,20*(i+1)+spacerHeight);
            portsProcI.push_back(port);
        }

        QJsonValue procO = obj["PO"];
        for(int i = 0;i < procO.toArray().size();++i){
            QJsonValue v = procO.toArray().at(i);
            GY_PortItem* port;
            port = new GY_PortItem(GY_PortItem::procO,v.toString());
            port->setParentItem(this);
            port->setPos(width-20,20*(i+1)+spacerHeight);
            portsProcO.push_back(port);
        }

        QJsonValue dataI = obj["DI"];
        for(int i = 0;i < dataI.toArray().size();++i){
            QJsonValue v = dataI.toArray().at(i);
            QString name;
            QString value;
            QString strSignals;
            QString description;
            if(v.isString()){
                name = v.toString();
            }else if(v.isObject()){
                name = v.toObject()["name"].toString();
                value = v.toObject()["value"].toString();
                strSignals = v.toObject()["signals"].toString();
                description = v.toObject()["description"].toString();
            }
            GY_PortItem* port;
            port = new GY_PortItem(GY_PortItem::dataI,name);
            port->setParentItem(this);
            port->SetValue(value);
            port->setStrSignals(strSignals);
            port->setDescription(description);
            port->setPos(10,20*(i+1+procI.toArray().size())+spacerHeight);
            portsDataI.push_back(port);
        }
/*Output只返回一个值
        if(myDiagramType==Output){
            //NOTE: DI "add" port
            PortItem* port;
            port = new PortItem(PortItem::add,"");
            port->SetAddToType(PortItem::dataI);
            port->setParentItem(this);
            port->setPos(10,20*(1+dataI.toArray().size()+procI.toArray().size())+spacerHeight);
            portsDataI.push_back(port);
        }
//*/

        QJsonValue dataO = obj["DO"];
        for(int i = 0;i < dataO.toArray().size();++i){
            QJsonValue v = dataO.toArray().at(i);
            QString name;
            QString value;
            if(v.isString()){
                name = v.toString();
            }else if(v.isObject()){
                name = v.toObject()["name"].toString();
                value = v.toObject()["value"].toString();
            }

            GY_PortItem* port;
            port = new GY_PortItem(GY_PortItem::dataO,name);
            port->setParentItem(this);
            port->SetValue(value);
            port->setPos(width-20,20*(i+1+procO.toArray().size())+spacerHeight);
            portsDataO.push_back(port);
            Higher(port);
        }
        if(myDiagramType==Input){
            //NOTE: DO "add" port
            GY_PortItem* port;
            port = new GY_PortItem(GY_PortItem::add,"");
            port->SetAddToType(GY_PortItem::dataO);
            port->setParentItem(this);
            port->setPos(width-20,20*(1+dataO.toArray().size()+procO.toArray().size())+spacerHeight);
            portsDataO.push_back(port);

            Higher(port);
        }

        QPointF positon;  //保存的位置点
        positon.setX(obj["x"].toDouble());
        positon.setY(obj["y"].toDouble());
        setPos(positon);  //
    }


//


}
//! [0]

//! [1]
void GY_DiagramItem::removeArrow(GY_Arrow *arrow)
{
    arrows.removeAll(arrow);
}
//! [1]

//! [2]
void GY_DiagramItem::removeArrows()
{
    // need a copy here since removeArrow() will
    // modify the arrows container
    const auto arrowsCopy = arrows;
    for (GY_Arrow *arrow : arrowsCopy) {
//        arrow->startItem()->removeArrow(arrow);
//        arrow->endItem()->removeArrow(arrow);
        arrow->startPort()->SetLineCount(-1);
        arrow->endPort()->SetLineCount(-1);
        ((GY_DiagramItem*)arrow->startPort()->parentItem())->removeArrow(arrow);
        ((GY_DiagramItem*)arrow->endPort()->parentItem())->removeArrow(arrow);
        scene()->removeItem(arrow);
        delete arrow;
        arrow = nullptr;
    }
}
//! [2]

//! [3]
void GY_DiagramItem::addArrow(GY_Arrow *arrow)
{
    arrows.append(arrow);
}
//! [3]

//! [4]
QPixmap GY_DiagramItem::image() const
{
    QPixmap pixmap(250, 250);
    pixmap.fill(Qt::transparent);
    QPainter painter(&pixmap);
    painter.setPen(QPen(Qt::black, 8));
    painter.translate(125, 125);
    painter.drawPolyline(myPolygon);

    return pixmap;
}
//! [4]

//! [5]
void GY_DiagramItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    scene()->clearSelection();
    setSelected(true);
    myContextMenu->exec(event->screenPos());
}
//! [5]

//! [6]
QVariant GY_DiagramItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
    if (change == QGraphicsItem::ItemPositionChange) {
        for (GY_Arrow *arrow : qAsConst(arrows))
            arrow->updatePosition();
    }

    return value;
}

QString GY_DiagramItem::getTips() const
{
    return tips;
}

QString GY_DiagramItem::getName() const
{
    return name;
}

void GY_DiagramItem::ItemChange(){
    itemChange(QGraphicsItem::ItemPositionChange, QVariant());
}

void GY_DiagramItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QGraphicsPolygonItem::paint(painter,option,widget);
    //this->brush().setColor();  //画刷颜色已在场景中设置
    QPen pen;
    pen.setWidth(1);
    pen.setColor(myTextColor);
    painter->setPen(pen);
    QFont font = painter->font();
    font.setPixelSize(myFontPixelSize);
    painter->setFont(font);
//    QImage image;
//    QBrush brush;
//    brush.setTextureImage(image);
    QColor bgColor;  //图元名称背景颜色
    switch (myDiagramType) {
    case StartEnd:
        bgColor = QColor(168,53,53,200);
        break;
    case Step:
        bgColor = QColor(101,162,203,200);
        break;
    case Branch:
        bgColor = QColor(203,203,203,200);
        break;
    case Input:
    case Output:
        bgColor = QColor(128,0,128,200);  //紫色
        break;
    default:
        break;
    }
    painter->fillRect(QRect(0,0,width,20),bgColor);
    QFontMetrics fm(painter->font());
    //painter->drawText(1,1+fm.height(),name);
    painter->drawText(0,0,width,20,Qt::AlignCenter,name);

    int spacerHeight = 10;
    //流程入口槽文字
    for(int i = 0; i < portsProcI.size();++i){
        painter->drawText(5+20,20*(i+2),portsProcI[i]->GetName()=="default"?"":portsProcI[i]->GetName());
    }
    //流程出口槽文字
    for(int i = 0; i < portsProcO.size();++i){
        painter->drawText(width/2-5,20*(i+1)+spacerHeight,width/2-20,20,Qt::AlignRight,portsProcO[i]->GetName()=="default"?"":portsProcO[i]->GetName());
    }
    //参数入口槽文字
    for(int i = 0; i < portsDataI.size();++i){
        painter->drawText(5+20,20*(i+2+portsProcI.size()),portsDataI[i]->GetName()=="default"?"":portsDataI[i]->GetName());
    }
    //参数出口槽文字
    for(int i = 0; i < portsDataO.size();++i){
        painter->drawText(width/2-5,20*(i+1+portsProcO.size())+spacerHeight,width/2-20,20,Qt::AlignRight,portsDataO[i]->GetName()=="default"?"":portsDataO[i]->GetName());
    }
}

void GY_DiagramItem::addPort(GY_PortItem::PortType type,GY_PortItem* addPort)
{
    int spacerHeight = 10;
    GY_PortItem* port;
    port = new GY_PortItem(type);
    QString name;
    switch (type) {
    case GY_PortItem::procI:
        break;
    case GY_PortItem::procO:
        break;
    case GY_PortItem::dataI:  //NOTE: Output item
        name = QString("Out%1").arg(GetPortsDataI().size());
        port->setPos(10,20*(GetPortsDataI().size()+GetPortsProcI().size())+spacerHeight);
        portsDataI.push_back(port);
        portsDataI.swapItemsAt(portsDataI.indexOf(port),portsDataI.indexOf(port)-1);
        break;
    case GY_PortItem::dataO:  //NOTE: Input item
        name = QString("In%1").arg(GetPortsDataO().size());
        port->setPos(width-20,20*(GetPortsDataO().size()+GetPortsProcO().size())+spacerHeight);
        portsDataO.push_back(port);
        portsDataO.swapItemsAt(portsDataO.indexOf(port),portsDataO.indexOf(port)-1);
        break;
    default:
        break;
    }
    port->SetName(name);
    port->setParentItem(this);

    //data port
    Higher(addPort);


    this->update();
}

void GY_DiagramItem::Higher(GY_PortItem* port){
    if(!boundingRect().contains(port->mapToParent(port->boundingRect().bottomRight()))){

        height += 20;
        QPolygonF Polygon;
        Polygon << QPointF(0,0) << QPointF(width, 0)
                  << QPointF(width, height) << QPointF(0, height)
                  << QPointF(0, 0);

        setPolygon(Polygon);

    }
}

